RAS: Add helpers to access Standard Error Records
authorJeenu Viswambharan <[email protected]>
Thu, 7 Dec 2017 08:43:05 +0000 (08:43 +0000)
committerJeenu Viswambharan <[email protected]>
Fri, 4 May 2018 07:33:17 +0000 (08:33 +0100)
The ARMv8 RAS Extensions introduced Standard Error Records which are a
set of standard registers through which:

  - Platform can configure RAS node policy; e.g., notification
    mechanism;

  - RAS nodes can record and expose error information for error handling
    agents.

Standard Error Records can either be accessed via. memory-mapped
or System registers. This patch adds helper functions to access
registers and fields within an error record.

Change-Id: I6594ba799f4a1789d7b1e45b3e17fd40e7e0ba5c
Signed-off-by: Jeenu Viswambharan <[email protected]>
include/lib/aarch64/arch.h
include/lib/aarch64/arch_helpers.h
include/lib/extensions/ras_arch.h [new file with mode: 0644]
include/lib/utils_def.h
lib/extensions/ras/std_err_record.c [new file with mode: 0644]

index 5be4b4e9edbf8125b718a4dffc7b5e66e672c7d5..e836db1e9f479f4f8fcb3f04539220402e7c93fe 100644 (file)
 #define DISR_EL1               S3_0_C12_C1_1
 #define DISR_A_BIT             31
 
+#define ERRIDR_EL1             S3_0_C5_C3_0
+#define ERRIDR_MASK            0xffff
+
+#define ERRSELR_EL1            S3_0_C5_C3_1
+
+/* System register access to Standard Error Record registers */
+#define ERXFR_EL1              S3_0_C5_C4_0
+#define ERXCTLR_EL1            S3_0_C5_C4_1
+#define ERXSTATUS_EL1          S3_0_C5_C4_2
+#define ERXADDR_EL1            S3_0_C5_C4_3
+#define ERXMISC0_EL1           S3_0_C5_C4_4
+#define ERXMISC1_EL1           S3_0_C5_C4_5
+
 #endif /* __ARCH_H__ */
index c346f7961f12c10ecb7fd8c517b7651051a082d4..58ec943a2d77979bd0a6fb5347a2d4a45dd23ec5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -333,6 +333,16 @@ DEFINE_RENAME_SYSREG_RW_FUNCS(pmblimitr_el1, PMBLIMITR_EL1)
 DEFINE_RENAME_SYSREG_WRITE_FUNC(zcr_el3, ZCR_EL3)
 DEFINE_RENAME_SYSREG_WRITE_FUNC(zcr_el2, ZCR_EL2)
 
+DEFINE_RENAME_SYSREG_READ_FUNC(erridr_el1, ERRIDR_EL1)
+DEFINE_RENAME_SYSREG_WRITE_FUNC(errselr_el1, ERRSELR_EL1)
+
+DEFINE_RENAME_SYSREG_READ_FUNC(erxfr_el1, ERXFR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(erxctlr_el1, ERXCTLR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(erxstatus_el1, ERXSTATUS_EL1)
+DEFINE_RENAME_SYSREG_READ_FUNC(erxaddr_el1, ERXADDR_EL1)
+DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc0_el1, ERXMISC0_EL1)
+DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc1_el1, ERXMISC1_EL1)
+
 #define IS_IN_EL(x) \
        (GET_EL(read_CurrentEl()) == MODE_EL##x)
 
diff --git a/include/lib/extensions/ras_arch.h b/include/lib/extensions/ras_arch.h
new file mode 100644 (file)
index 0000000..7d21053
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __RAS_H__
+#define __RAS_H__
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <context.h>
+#include <mmio.h>
+#include <stdint.h>
+
+/*
+ * Size of nodes implementing Standard Error Records - currently only 4k is
+ * supported.
+ */
+#define STD_ERR_NODE_SIZE_NUM_K                4
+
+/*
+ * Individual register offsets within an error record in Standard Error Record
+ * format when error records are accessed through memory-mapped registers.
+ */
+#define ERR_FR(n)      (0x0 + (64 * (n)))
+#define ERR_CTLR(n)    (0x8 + (64 * (n)))
+#define ERR_STATUS(n)  (0x10 + (64 * (n)))
+#define ERR_ADDR(n)    (0x18 + (64 * (n)))
+#define ERR_MISC0(n)   (0x20 + (64 * (n)))
+#define ERR_MISC1(n)   (0x28 + (64 * (n)))
+
+/* Group Status Register (ERR_STATUS) offset */
+#define ERR_GSR(base, size_num_k, n) \
+       ((base) + (0x380 * (size_num_k)) + (8 * (n)))
+
+/* Management register offsets */
+#define ERR_DEVID(base, size_num_k) \
+       ((base) + ((0x400 * (size_num_k)) - 0x100) + 0xc8)
+
+#define ERR_DEVID_MASK 0xffff
+
+/* Standard Error Record status register fields */
+#define ERR_STATUS_AV_SHIFT    31
+#define ERR_STATUS_AV_MASK     U(0x1)
+
+#define ERR_STATUS_V_SHIFT     30
+#define ERR_STATUS_V_MASK      U(0x1)
+
+#define ERR_STATUS_UE_SHIFT    29
+#define ERR_STATUS_UE_MASK     U(0x1)
+
+#define ERR_STATUS_ER_SHIFT    28
+#define ERR_STATUS_ER_MASK     U(0x1)
+
+#define ERR_STATUS_OF_SHIFT    27
+#define ERR_STATUS_OF_MASK     U(0x1)
+
+#define ERR_STATUS_MV_SHIFT    26
+#define ERR_STATUS_MV_MASK     U(0x1)
+
+#define ERR_STATUS_CE_SHIFT    24
+#define ERR_STATUS_CE_MASK     U(0x3)
+
+#define ERR_STATUS_DE_SHIFT    23
+#define ERR_STATUS_DE_MASK     U(0x1)
+
+#define ERR_STATUS_PN_SHIFT    22
+#define ERR_STATUS_PN_MASK     U(0x1)
+
+#define ERR_STATUS_UET_SHIFT   20
+#define ERR_STATUS_UET_MASK    U(0x3)
+
+#define ERR_STATUS_IERR_SHIFT  8
+#define ERR_STATUS_IERR_MASK   U(0xff)
+
+#define ERR_STATUS_SERR_SHIFT  0
+#define ERR_STATUS_SERR_MASK   U(0xff)
+
+#define ERR_STATUS_GET_FIELD(_status, _field) \
+       (((_status) >> ERR_STATUS_ ##_field ##_SHIFT) & ERR_STATUS_ ##_field ##_MASK)
+
+#define ERR_STATUS_CLR_FIELD(_status, _field) \
+       (_status) &= ~(ERR_STATUS_ ##_field ##_MASK << ERR_STATUS_ ##_field ##_SHIFT)
+
+#define ERR_STATUS_SET_FIELD(_status, _field, _value) \
+       (_status) |= (((_value) & ERR_STATUS_ ##_field ##_MASK) << ERR_STATUS_ ##_field ##_SHIFT)
+
+#define ERR_STATUS_WRITE_FIELD(_status, _field, _value) do { \
+               ERR_STATUS_CLR_FIELD(_status, _field, _value); \
+               ERR_STATUS_SET_FIELD(_status, _field, _value); \
+       } while (0)
+
+
+/* Standard Error Record control register fields */
+#define ERR_CTLR_WDUI_SHIFT    11
+#define ERR_CTLR_WDUI_MASK     0x1
+
+#define ERR_CTLR_RDUI_SHIFT    10
+#define ERR_CTLR_RDUI_MASK     0x1
+#define ERR_CTLR_DUI_SHIFT     ERR_CTLR_RDUI_SHIFT
+#define ERR_CTLR_DUI_MASK      ERR_CTLR_RDUI_MASK
+
+#define ERR_CTLR_WCFI_SHIFT    9
+#define ERR_CTLR_WCFI_MASK     0x1
+
+#define ERR_CTLR_RCFI_SHIFT    8
+#define ERR_CTLR_RCFI_MASK     0x1
+#define ERR_CTLR_CFI_SHIFT     ERR_CTLR_RCFI_SHIFT
+#define ERR_CTLR_CFI_MASK      ERR_CTLR_RCFI_MASK
+
+#define ERR_CTLR_WUE_SHIFT     7
+#define ERR_CTLR_WUE_MASK      0x1
+
+#define ERR_CTLR_WFI_SHIFT     6
+#define ERR_CTLR_WFI_MASK      0x1
+
+#define ERR_CTLR_WUI_SHIFT     5
+#define ERR_CTLR_WUI_MASK      0x1
+
+#define ERR_CTLR_RUE_SHIFT     4
+#define ERR_CTLR_RUE_MASK      0x1
+#define ERR_CTLR_UE_SHIFT      ERR_CTLR_RUE_SHIFT
+#define ERR_CTLR_UE_MASK       ERR_CTLR_RUE_MASK
+
+#define ERR_CTLR_RFI_SHIFT     3
+#define ERR_CTLR_RFI_MASK      0x1
+#define ERR_CTLR_FI_SHIFT      ERR_CTLR_RFI_SHIFT
+#define ERR_CTLR_FI_MASK       ERR_CTLR_RFI_MASK
+
+#define ERR_CTLR_RUI_SHIFT     2
+#define ERR_CTLR_RUI_MASK      0x1
+#define ERR_CTLR_UI_SHIFT      ERR_CTLR_RUI_SHIFT
+#define ERR_CTLR_UI_MASK       ERR_CTLR_RUI_MASK
+
+#define ERR_CTLR_ED_SHIFT      0
+#define ERR_CTLR_ED_MASK       0x1
+
+#define ERR_CTLR_CLR_FIELD(_ctlr, _field) \
+       (_ctlr) &= ~(ERR_CTLR_ ##_field _MASK << ERR_CTLR_ ##_field ##_SHIFT)
+
+#define ERR_CTLR_SET_FIELD(_ctlr, _field, _value) \
+       (_ctlr) |= (((_value) & ERR_CTLR_ ##_field ##_MASK) << ERR_CTLR_ ##_field ##_SHIFT)
+
+#define ERR_CTLR_ENABLE_FIELD(_ctlr, _field) \
+       ERR_CTLR_SET_FIELD(_ctlr, _field, ERR_CTLR_ ##_field ##_MASK)
+
+/* Uncorrected error types */
+#define ERROR_STATUS_UET_UC    0x0     /* Uncontainable */
+#define ERROR_STATUS_UET_UEU   0x1     /* Unrecoverable */
+#define ERROR_STATUS_UET_UEO   0x2     /* Restable */
+#define ERROR_STATUS_UET_UER   0x3     /* Recoverable */
+
+
+/*
+ * Standard Error Record accessors for memory-mapped registers.
+ */
+
+static inline uint64_t ser_get_feature(uintptr_t base, unsigned int idx)
+{
+       return mmio_read_64(base + ERR_FR(idx));
+}
+
+static inline uint64_t ser_get_control(uintptr_t base, unsigned int idx)
+{
+       return mmio_read_64(base + ERR_CTLR(idx));
+}
+
+static inline uint64_t ser_get_status(uintptr_t base, unsigned int idx)
+{
+       return mmio_read_64(base + ERR_STATUS(idx));
+}
+
+/*
+ * Error handling agent would write to the status register to clear an
+ * identified/handled error. Most fields in the status register are
+ * conditional write-one-to-clear.
+ *
+ * Typically, to clear the status, it suffices to write back the same value
+ * previously read. However, if there were new, higher-priority errors recorded
+ * on the node since status was last read, writing read value won't clear the
+ * status. Therefore, an error handling agent must wait on and verify the status
+ * has indeed been cleared.
+ */
+static inline void ser_set_status(uintptr_t base, unsigned int idx,
+               uint64_t status)
+{
+       mmio_write_64(base + ERR_STATUS(idx), status);
+}
+
+static inline uint64_t ser_get_addr(uintptr_t base, unsigned int idx)
+{
+       return mmio_read_64(base + ERR_ADDR(idx));
+}
+
+static inline uint64_t ser_get_misc0(uintptr_t base, unsigned int idx)
+{
+       return mmio_read_64(base + ERR_MISC0(idx));
+}
+
+static inline uint64_t ser_get_misc1(uintptr_t base, unsigned int idx)
+{
+       return mmio_read_64(base + ERR_MISC1(idx));
+}
+
+
+/*
+ * Standard Error Record helpers for System registers.
+ */
+static inline void ser_sys_select_record(unsigned int idx)
+{
+       unsigned int max_idx __unused = read_erridr_el1() & ERRIDR_MASK;
+
+       assert(idx < max_idx);
+
+       write_errselr_el1(idx);
+       isb();
+}
+
+/* Library functions to probe Standard Error Record */
+int ser_probe_memmap(uintptr_t base, unsigned int size_num_k, int *probe_data);
+int ser_probe_sysreg(unsigned int idx_start, unsigned int num_idx, int *probe_data);
+
+#endif /* __RAS_H__ */
index 8abc73c093c45d6ef97b0f4179be0b49d359d175..31b129454a0dbf83b4aad31ee6f460293e127462 100644 (file)
 #define check_uptr_overflow(ptr, inc)          \
        (((ptr) > UINTPTR_MAX - (inc)) ? 1 : 0)
 
+/*
+ * Evaluates to 1 if (u32 + inc) overflows, 0 otherwise.
+ * Both arguments must be 32-bit unsigned integers (i.e. effectively uint32_t).
+ */
+#define check_u32_overflow(u32, inc) \
+       ((u32) > (UINT32_MAX - (inc)) ? 1 : 0)
+
 /*
  * For those constants to be shared between C and other sources, apply a 'u'
  * or 'ull' suffix to the argument only in C, to avoid undefined or unintended
diff --git a/lib/extensions/ras/std_err_record.c b/lib/extensions/ras/std_err_record.c
new file mode 100644 (file)
index 0000000..65c007f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <ras_arch.h>
+#include <utils_def.h>
+
+/*
+ * Probe for error in memory-mapped registers containing error records
+ * implemented Standard Error Record format. Upon detecting an error, set probe
+ * data to the index of the record in error, and return 1; otherwise, return 0.
+ */
+int ser_probe_memmap(uintptr_t base, unsigned int size_num_k, int *probe_data)
+{
+       int num_records, num_group_regs, i;
+       uint64_t gsr;
+
+       assert(base != 0);
+
+       /* Only 4K supported for now */
+       assert(size_num_k == STD_ERR_NODE_SIZE_NUM_K);
+
+       num_records = (mmio_read_32(ERR_DEVID(base, size_num_k)) & ERR_DEVID_MASK);
+
+       /* A group register shows error status for 2^6 error records */
+       num_group_regs = (num_records >> 6) + 1;
+
+       /* Iterate through group registers to find a record in error */
+       for (i = 0; i < num_group_regs; i++) {
+               gsr = mmio_read_64(ERR_GSR(base, size_num_k, i));
+               if (gsr == 0)
+                       continue;
+
+               /* Return the index of the record in error */
+               if (probe_data != NULL)
+                       *probe_data = ((i << 6) + __builtin_ctz(gsr));
+
+               return 1;
+       }
+
+       return 0;
+}
+
+/*
+ * Probe for error in System Registers where error records are implemented in
+ * Standard Error Record format. Upon detecting an error, set probe data to the
+ * index of the record in error, and return 1; otherwise, return 0.
+ */
+int ser_probe_sysreg(unsigned int idx_start, unsigned int num_idx, int *probe_data)
+{
+       int i;
+       uint64_t status;
+       unsigned int max_idx __unused = read_erridr_el1() & ERRIDR_MASK;
+
+       assert(idx_start < max_idx);
+       assert(check_u32_overflow(idx_start, num_idx) == 0);
+       assert((idx_start + num_idx - 1) < max_idx);
+
+       for (i = 0; i < num_idx; i++) {
+               /* Select the error record */
+               ser_sys_select_record(idx_start + i);
+
+               /* Retrieve status register from the error record */
+               status = read_erxstatus_el1();
+
+               /* Check for valid field in status */
+               if (ERR_STATUS_GET_FIELD(status, V)) {
+                       if (probe_data != NULL)
+                               *probe_data = i;
+                       return 1;
+               }
+       }
+
+       return 0;
+}